﻿/*
 * Mike McBride & Dave Rank's
 * SE-4910 Final
 * Stop Watch / Timer Mobile Application
 * due: 25 Feb 10
 */

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Reflection;
using System.Xml; //Needed for XML parsing
using System.Text; //Needed for StringBuilder


namespace StopWatch.View
{
    using Controller;

    public partial class StopWatchUI : Form, IStopWatchUI
    {
        private readonly StopWatch _stopWatch;

        // Timer positioning properties
        // These ratios approximate the center of the stopwatch, regardless of its dimensions
        private const double HorizontalProportion = 0.52;
        private const double VerticalProportion = 0.63;

        string _path = @"StopWatchSettings.txt";

        public StopWatchUI()
        {
            InitializeComponent();
            _stopWatch = new StopWatch(this);


            //Read and Parse Settings File
            bool _useDefaultUser = false;
            StringBuilder output = new StringBuilder();
            
            if (File.Exists(_path))
            {
                //Bring in the whole file
                string s = "";
                using (StreamReader sr = File.OpenText(_path))
                {
                    string l = ""; //create a temp Line variable
                    while ((l = sr.ReadLine()) != null) //read the line
                    {
                        s += l; //add the line to the accumulating string
                    }
                }

                //Parse the file
                using (XmlReader reader = XmlReader.Create(new StringReader(s)))
                {
                    string x = ""; //temp variable
                    //int index = 0; //initialize the index
                    bool bIfUseDefaultLoaded = false;
                    while (reader.Read()) //read next xml item
                    {
                        if (bIfUseDefaultLoaded) break; //if defaults are desired, leave loop
                        //TD: may have to close file nicely

                        switch (reader.NodeType) //set test property
                        {
                            case XmlNodeType.Element: //if an element
                                switch (reader.Name) //set to test for name
                                {
                                    case "username": //if username begin tag
                                        reader.Read(); //get next item; presumed the string
                                        x = reader.Value.ToString(); //convert to string

                                        //try finding name in list and setting value in class
                                        try
                                        {
                                            //TD: this should find it, but there's no such method
                                            //index = cboUserName.FindString(x);
                                            //cboUserName.SelectedIndex = index;
                                            //so... I had to write all this next stuff...

                                            //Find the index of the corresponding item in the combobox
                                            bool bFound = false; //initialize to not found
                                            int i = 0; //sync counter variable with loop
                                            foreach (Object item in cboUserName.Items) //for all the items in the pulldown
                                            {
                                                if (x == item.ToString()) //if there's a name match
                                                {
                                                    bFound=true; //flag that a match has been found
                                                    break; //leave loop retaining counter value
                                                }
                                                i++; //if not, increment counter and keep going
                                            } //endfor
                                            if (bFound) //if a match was found
                                            {                
                                                cboUserName.SelectedIndex = i; //force control to select that item
                                                _stopWatch.Username = cboUserName.Text; //put item text in class value
                                            }
                                            else //if no match was found
                                            {
                                                _useDefaultUser = true; //flag to use the defaults
                                            }
                                        }
                                        catch //if anything goes wrong
                                        {
                                            _useDefaultUser = true; //flag to use the defaults
                                        }

                                        break;
                                    case "rememberlastuser": //begin tag
                                        reader.Read(); //read the value
                                        x = reader.Value.ToString(); //make it a string
                                        if (x == "yes") //if it was yes
                                        {
                                            chkRememberLastUser.Checked = true; //check the box of the setting
                                        }
                                        else //if any other value
                                        {
                                            chkRememberLastUser.Checked = false; //uncheck the box
                                            _useDefaultUser = true;
                                            bIfUseDefaultLoaded = true;
                                        }
                                        break;
                                    default:
                                        break;
                                }
                                break;
                            case XmlNodeType.Text:
                                break;
                            case XmlNodeType.XmlDeclaration:
                                break;
                            case XmlNodeType.EndElement:
                                break;
                            default:
                                _useDefaultUser=true;
                                break;
                        }//end switch
                    }//end while
                }//end using xmlreader
            }
            else
            {
                _useDefaultUser=true;
            }

            //If something went wrong reading settings file, use default
            if (_useDefaultUser)
            {
                //_stopWatch.Username = "user name";
                
                chkRememberLastUser.Checked = false; //Uncheck box, because no file exists
                cboUserName.SelectedIndex = 0; //Use first item in list as default user

                //Change event is automatically triggered, and UserName initialized
                //_stopWatch.Username = cboUserName.Text; //Initialize user name <-- can be deleted
            }

            _stopWatch.LoggingEnabled = false;

        }

        //DON'T DELETE - will not compile without this class
        private void tabTimer_Click(object sender, EventArgs e)
        {
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            //MessageBox.Show("Disabled, at the moment");

            if (_stopWatch.TimerStopped) //If the timer is not running
            {
                mnuLeft_Click(sender, e); //Start using the soft key choice
                //The Start branch will be used
            }
            //else, ignore the button press
        }//end btnStart_Click


        private void btnStop_Click(object sender, EventArgs e)
        {
            if (!_stopWatch.TimerStopped) //If the timer is running
            {
                mnuLeft_Click(sender, e); //Stop by using the soft key choice
                //The Stop branch will be used
            }
            //else, ignore the button press
        }//end btnStop_Click


        private void StopWatchUI_Closing(object sender, CancelEventArgs e)
        {
            //If not ready to stop, tell user closing is not allowed
            if (!_stopWatch.TimerStopped) //If not ready to stop
            {
                MessageBox.Show("Stop clock first."); //Warn user
                e.Cancel = true; //Cancel closing event handler
            }
            else
            {
                //if(File.Exists(_path))
                //{
                    //Create or Overwrite the settings file with a new one
                    using (StreamWriter sw = File.CreateText(_path))
                    {
                        sw.WriteLine("<Items>");

                        //write the appropriate setting for the checkbox
                        if (chkRememberLastUser.Checked == true)
                        {
                            sw.WriteLine("<rememberlastuser>yes</rememberlastuser>");
                        }
                        else
                        {
                            sw.WriteLine("<rememberlastuser>no</rememberlastuser>");
                        }

                        sw.WriteLine("<username>" + 
                            cboUserName.Text +
                            "</username>");
                        sw.WriteLine("</Items>");
                    }
                }//end timer running check
            } //end StopWatchUI_Closing

        private delegate void ControlUpdater(Control ui, string text);

        //Method for updating control
        private void UpdateControl( Control uiControl, string text)
        {
            uiControl.Text = text; //Put the text string in the text property of the given control
        }

        /// <summary>
        /// Method for refreshing form items to redraw dynamic graphics.
        /// </summary>
        public void RefreshThis()
        {
            // TODO Invoke
            //RePaintTimerHand();
        }

        public void UpdateStartDisplay()
        {
            lblStart.Text = _stopWatch.StartTime.ToLongTimeString(); //Display captured time in field
        }

        public void UpdateStopDisplay()
        {
            DateTime stoppedTime = _stopWatch.EndTime; //Store difference between current and start times
            ControlUpdater c = UpdateControl;

            Invoke(c, lblStop, stoppedTime.ToLongTimeString()); //Update the display in the label control
        }

        public void UpdateElapsedDisplay()
        {
            if(InvokeRequired)
            {
                Invoke(new ThreadStart(UpdateElapsedDisplay));
                return;
            }

            TimeSpan elapsedTime = _stopWatch.ElapsedTime; // Store difference between current and start times
            lblElapsed.Text = elapsedTime.ToString(); // Update the display in the label control
            RePaintTimerHand(); // Refresh the timer hand
        }

        public void UpdateLapDisplay()
        {
            TimeSpan lapTime = _stopWatch.LapTime;
            ControlUpdater c = UpdateControl;

            Invoke(c, lblLap, lapTime.ToString());
        }


        //Handling of left soft button, which has multiple modes
        private void mnuLeft_Click(object sender, EventArgs e)
        {
            //Toggle start/stop functionality
            if (_stopWatch.TimerStopped) //If timer is stopped
            {
                //Start the timer
                _stopWatch.Start();

                lblStop.Text = ""; //Clear the stop time
                lblLap.Text = ""; //Clear lap time
                mnuLeft.Text = "Stop"; //Flip button to be ready for next command
                mnuRight.Text = "Lap"; //Flip to Lap-only, button
            }
            else //If the timer is running
            { 
                //Stop timer and calculate final duration, if not already stopped
                _stopWatch.Stop();

                mnuLeft.Text = "Start"; //Flip button to be ready for next command
                mnuRight.Text = "Reset"; //Flip to Reset button 

                //Make last update to Lap time, if used
                UpdateLapDisplay();
            } //end check whether Start or Stop
        } //end mnuLeft_Click


        //Handling of right soft button, which has multiple modes
        private void mnuRight_Click(object sender, EventArgs e)
        {
            if (_stopWatch.TimerStopped) //If timer stopped, assume to be ready for Reset
            {
                //Perform the Reset of display
                //Clear the displayed time labels
                lblStart.Text = "";
                lblStop.Text = "";
                lblLap.Text = "";
                lblElapsed.Text = "";
                picTimerFace.Refresh();
                _stopWatch.Reset();
            }
            else //if the timer is running
            {
                _stopWatch.Lap();
            }//end check if Lap or Reset
        }//end mnuRight_Click


        //Loading the Main Form
        private void StopWatchUI_Load(object sender, EventArgs e)
        {
            //Clear the timer values (kept in the labels for visibility during development)
            lblStart.Text = "";
            lblStop.Text = "";
            lblLap.Text = "";
            lblElapsed.Text = "";

            try
            {
                tabTimer.Show(); //Show the timer tab first

                //Resize and Position Timer Face to fill the tab control
                int screenW = ClientRectangle.Width;
                int screenH = ClientRectangle.Height;
                int tabH = 20; //Approximate tab height
                int tabVizPageW = tabControl1.Width;
                int tabVizPageH = tabControl1.Height - tabH;
                int imgW = picTimerFace.Image.Width;
                int imgH = picTimerFace.Image.Height;

                //For development reporting
                //MessageBox.Show("Screen width = " + screenW +
                //                ",\nScreen height = " + screenH +
                //                ",\nImage width = " + imgW +
                //                ",\nImage height = " + imgH);

                //Put Timer Picture horizontally centered and showing full height
                //if ((screenH >= screenW) && (imgH >= imgW)) //if screen and image are portrait orientation
                if ((tabVizPageH >= tabVizPageW) && (imgH >= imgW)) //if screen and image are portrait orientation
                {
                    //Resize picture to fit visible tab control
                    picTimerFace.Top = 0; //Always place picture at top
                    picTimerFace.Height = tabVizPageH; //Force height to tab control height
                    picTimerFace.Width = (int)((double)picTimerFace.Height / imgH * imgW); //Keep aspect ratio; scale width; account for status bar height
                    picTimerFace.Left = (tabVizPageW - picTimerFace.Width) / 2; //center image
                }
                else if ((tabVizPageW > tabVizPageH) && (imgH >= imgW)) //if screen is landscape, but image is portrait
                {
                    //Resize picture to fit visible tab control
                    picTimerFace.Top = 0; //Always place picture at top
                    picTimerFace.Height = tabVizPageH; //Force height to tab control height
                    picTimerFace.Width = (int)((double)picTimerFace.Height / imgH * imgW); //Keep aspect ratio; scale width; account for status bar height
                    picTimerFace.Left = (tabVizPageW - picTimerFace.Width) / 2; //center image
                }
                else //If some other orientation
                {
                    //Default placement; no sizing
                    picTimerFace.Top = 0;  
                    picTimerFace.Height = imgH;
                    picTimerFace.Width = imgW;
                    picTimerFace.Left = 0;
                }

                //TD: Deal with screen orientation in a future release
            }
            catch
            {
                //If something goes wrong, display currently directory, where image should be
                string strAppDir = 
                    Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
                MessageBox.Show("Current directory: " + strAppDir);
            }
        }//end Main form load


        //Handle changes event of txtNrIntervals
        private void txtNrIntervals_TextChanged_1(object sender, EventArgs e)
        {
            //Calculate the duration of an interval, if a total duration exists
            try
            {
                //Calculate the new interval duration from the total duration and
                //number of intervals
                txtIntervalDur.Text =
                    Convert.ToString(
                        Convert.ToDouble(txtTotalTime.Text) /
                        Convert.ToDouble(txtNrIntervals.Text)
                        ); //Convert entry to value
                //If a non-numeric value is entered, an error will be thrown to the catch clause
            }
            catch
            {
                txtNrIntervals.Text = ""; //Clear value
            }
        }//end update of txtNrIntervals


        //Handle changes to txtTotalTime
        private void txtTotalTime_TextChanged_1(object sender, EventArgs e)
        {
            try
            {
                txtIntervalDur.Text =
                    Convert.ToString(
                        Convert.ToDouble(txtTotalTime.Text) /
                        Convert.ToDouble(txtNrIntervals.Text)
                        ); //Convert entry to value
                //If a non-numeric value is entered, an error will be thrown to the catch clause
            }
            catch
            {
                //Do nothing
            }
        }//end update of txtTotalTime


        /// <summary>
        /// Paints the control.
        /// Base code adapted from online example
        /// </summary>
        protected void RePaintTimerHand()
        {
            int penWidth = 5;
            using (Graphics g = picTimerFace.CreateGraphics())
            using (Pen penRed = new Pen(Color.Red, penWidth - 2))
            {
                picTimerFace.Refresh();
                //double radius = (this.picTimerFace.Width) / 2;
                double radiusInner = (picTimerFace.Width - 30) / 2;
                int xcenter = (int)(picTimerFace.Width * HorizontalProportion);
                int ycenter = (int)(picTimerFace.Height * VerticalProportion);

                // Draw timer hand
                double timerAngle = -_stopWatch.ElapsedTime.Seconds * 12 + 180;
                double timerX = Math.Sin(ToRadian(timerAngle)) * radiusInner;
                double timerY = Math.Cos(ToRadian(timerAngle)) * radiusInner;
                g.DrawLine(penRed, (int)timerX + xcenter, (int)timerY + ycenter, xcenter, ycenter);
            }
        }

        /// <summary>
        /// Converts an angle value to a radian value.
        /// </summary>
        /// <param name="angle">The angle (0.0 to 360.0)</param>
        /// <returns>A double radian value (0 to 2 * Math.PI)</returns>
        protected double ToRadian(double angle)
        {
            double retVal = angle * Math.PI / 180;

            return retVal;
        }


        //Handle Settings mode button press
        private void btnMode_Click(object sender, EventArgs e)
        {
            if (_stopWatch.TimerStopped) //If the timer is not running
            {
                //Toggle the direction the timer counts: up or down
                //Enable or disable appropriate fields
                if (_stopWatch.CountUp) //If direction is up
                {
                    _stopWatch.CountUp = false; //Flip to down
                    //TD: Ask for a value to start from
                    btnMode.Text = "Count Down";
                    txtTotalTime.Enabled = true;
                    txtNrIntervals.Enabled = true;
                    lblTotalTime.Enabled = true;
                    lblNrIntervals.Enabled = true;
                }
                else
                {
                    _stopWatch.CountUp = true; //Flip to up
                    btnMode.Text = "Count Up";
                    txtTotalTime.Enabled = false;
                    txtNrIntervals.Enabled = false;
                    lblTotalTime.Enabled = false;
                    lblNrIntervals.Enabled = false;
                }
            }
            else
            {
                MessageBox.Show("Stop clock first."); //Warn user
            }
        }

        private void chkWebLogging_CheckStateChanged(object sender, EventArgs e)
        {
            //Toggle true / false logging flag 
            _stopWatch.LoggingEnabled = _stopWatch.LoggingEnabled ? false : true;
            //Assumes initialization is false and checkbox is unchecked
        }

        private void cboUserName_SelectedIndexChanged(object sender, EventArgs e)
        {
            _stopWatch.Username = cboUserName.Text;
        }

        private void btnResetWebLog_Click(object sender, EventArgs e)
        {
            //Initializes the variables to pass to the MessageBox.Show method.
            string message = "This operation will clear all existing entries from the web log.\n\n" +
                             "Are you sure you want to do this?";
            string caption = "Clear ALL Log Entries";
            MessageBoxButtons buttons = MessageBoxButtons.YesNo;
            DialogResult result;

            //Displays the MessageBox.
            result = MessageBox.Show(message, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);

            //Decide action to take
            if (result == System.Windows.Forms.DialogResult.Yes)
            {
                //MessageBox.Show("Log entries deleted");
                _stopWatch.ResetLog();
            }
            else
            {
                MessageBox.Show("No action taken.");
            }
            
        }
    }
}